﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Text;
using TheVerge.Base.Platform;

namespace TheVerge.Base
{
    public static class API
    {
        private static ILocalStorage _storage;
        
        public static void Initialize(ILocalStorage storage, Action onFinished)
        {
            _storage = storage;
            _storage.LoadSetting(SESSION_SETTINGS_KEY, key => 
            { 
                SESSION_KEY = (string)key;
                onFinished();
            }, 
            error => onFinished());
        }


        #region Comments
        public static void GetComments(long articleId, Action<Comments> OnResult, Action<Exception> OnError)
        {
            var url = string.Format(COMMENTS_ALL, articleId, API_KEY);
            MakeRequest(url, OnResult, OnError);
        }
        
        public static void AddComment(long articleId, long? parentId, string comment, Action<Comment> OnResult, Action<Exception> OnError)
        {
            if (string.IsNullOrEmpty(SESSION_KEY))
            {
                OnError(new UnauthorizedAccessException(@"You have to be logged in to comment."));
                return;
            }

            var url = string.Format(COMMENTS_ADD, articleId, API_KEY);

            var body = new AddComment();
            body.CommentBody = comment;
            body.ParentId = parentId;
            body.SessionToken = SESSION_KEY;

            var serializer = new DataContractJsonSerializer(typeof(AddComment));
            using (var ms = new MemoryStream())
            {
                serializer.WriteObject(ms, body);
                ms.Position = 0;
                using (var reader = new StreamReader(ms))
                {
                    var bodyJson = reader.ReadToEnd();
                    MakeRequest<CommentWrapper>(url, bodyJson, wrapper => { OnResult(wrapper.Comment); }, OnError);
                }
            }
        }
        #endregion

        #region Forums Entries
        public static void GetForums(Action<Forums> OnResult, Action<Exception> OnError)
        {
            var url = GetUrl("forums");
            MakeRequest(url, OnResult, OnError);
        }

        public static void GetForumActive(int page, int forumId, Action<APIResponse<ArticleEntry>> OnResult, Action<Exception> OnError)
        {
            var url = GetUrl("entries", page) + @"forum_sort=active&html5_video=true&forum_ids=" + forumId;
            MakeRequest(url, OnResult, OnError);
        }
        public static void GetForumRecent(int page, int forumId, Action<APIResponse<ArticleEntry>> OnResult, Action<Exception> OnError)
        {
            var url = GetUrl("entries", page) + @"forum_sort=recent&html5_video=true&forum_ids=" + forumId;
            MakeRequest(url, OnResult, OnError);
        }
        public static void GetForumPopular(int page, int forumId, Action<APIResponse<ArticleEntry>> OnResult, Action<Exception> OnError)
        {
            var url = GetUrl("entries", page) + @"forum_sort=popular&html5_video=true&forum_ids=" + forumId;
            MakeRequest(url, OnResult, OnError);
        }
        #endregion

        #region Media
        public static void GetGalleries(int page, Action<APIResponse<GalleryEntry>> OnResult, Action<Exception> OnError)
        {
            var url = GetUrl("galleries", page);
            GetEntries(url, OnResult, OnError);
        }

        /// <summary>
        /// http://cdn0.sbnation.com/api/v2/entries/photos?api_key=rTvtu68O91ctXH9GHJVFlyxUtWjcsnuo&iphone_client_version=2&entry_id=3008671&width=640&height=216&format=png&compress=true
        /// </summary>
        public static string GetGalleryPhotoUrl(int EntryId, int Width, int Height)
        {
            return string.Format(CDN_BASE, API_KEY, EntryId, Width, Height);
        }

        public static string GetImage(Entry e, int width, int height)
        {
            return string.Format(CDN_BASE, API_KEY, e.Id, width, height);
        }
        public static string GetImageSmall(Entry e)
        {
            return GetImage(e, 132, 132);
        }
        public static string GetImageLarge(Entry e)
        {
            return GetImage(e, 480, 300);
        }

        public static void GetPodcasts(int page, Action<APIResponse<PodcastEntry>> OnResult, Action<Exception> OnError)
        {
            var url = GetUrl("podcasts", page);
            GetEntries(url, OnResult, OnError);
        }

        public static void GetVideos(int page, Action<APIResponse<VideoEntry>> OnResult, Action<Exception> OnError)
        {
            var url = GetUrl("videos", page);
            GetEntries(url, OnResult, OnError);
        }
        #endregion

        #region News
        public static void GetFeatured(int page, Action<APIResponse<ArticleEntry>> OnResult, Action<Exception> OnError)
        {
            var url = GetUrl("entries", page) + @"&main_content=true&entry_types=Feature&lax_filtering=true&html5_video=true";
            GetEntries(url, OnResult, OnError);
        }

        public static void GetLatest(int page, Action<APIResponse<ArticleEntry>> OnResult, Action<Exception> OnError)
        {
            var url = GetUrl("entries", page) + @"&main_content=true&html5_video=true";
            GetEntries(url, OnResult, OnError);
        }

        public static void GetReviews(int page, Action<APIResponse<ArticleEntry>> OnResult, Action<Exception> OnError)
        {
            var url = GetUrl("entries", page) + @"&entry_types=ProductReview&main_content=true";
            GetEntries(url, OnResult, OnError);
        }
        #endregion

        #region Sections
        public static void GetBrands(Action<Brands> OnResult, Action<Exception> OnError)
        {
            var url = GetUrl(@"brands");
            MakeRequest(url, OnResult, OnError);
        }

        public static void GetBrandEntries(int page, int brandId, Action<APIResponse<ArticleEntry>> OnResult, Action<Exception> OnError)
        {
            var url = GetUrl(@"entries", page) + @"&html5_video=true&brand_ids[]=" + brandId;
            MakeRequest(url, OnResult, OnError);
        }


        public static void GetCategories(Action<Categories> OnResult, Action<Exception> OnError)
        {
            var url = GetUrl(@"categories");
            MakeRequest(url, OnResult, OnError);
        }

        public static void GetCategoryEntries(int page, int categoryid, Action<APIResponse<ArticleEntry>> OnResult, Action<Exception> OnError)
        {
            var url = GetUrl(@"entries", page) + @"&html5_video=true&category_ids[]=" + categoryid;
            MakeRequest(url, OnResult, OnError);
        }

        public static void GetHubs(Action<Hubs> OnResult, Action<Exception> OnError)
        {
            var url = GetUrl(@"hubs");
            MakeRequest(url, OnResult, OnError);
        }

        public static void GetHubEntries(int page, int hubId, Action<APIResponse<ArticleEntry>> OnResult, Action<Exception> OnError)
        {
            var url = GetUrl(@"entries", page) + @"&html5_video=true&hub_ids[]=" + hubId;
            MakeRequest(url, OnResult, OnError);
        }
        #endregion

        #region Authentication
        private static string SESSION_KEY;
        private static CookieContainer cookies = new CookieContainer();
        private const string SUCCESS_BEGIN = @"location.href = 'verge-iphone://authorize?session_key=";
        private const string SUCCESS_END = @"';";
        public static void Login(string userName, string password, Action onSucces, Action onError)
        {
            var url = @"https://www.theverge.com/sessions";
            var request = HttpWebRequest.CreateHttp(url);
            request.Method = "POST";
            request.ContentType = @"application/x-www-form-urlencoded";
            request.Headers["Referer"] = AUTH_REFERAL_URL;
            request.Headers["Origin"] = @"https://www.theverge.com";
            request.Headers[HttpRequestHeader.UserAgent] = @"Mozilla/5.0 (iPad; CPU OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/11A465(iPhone Vox)";
            

            request.CookieContainer = cookies;

            request.BeginGetRequestStream(result =>
            {
                using (var stream = request.EndGetRequestStream(result))
                using (var writer = new StreamWriter(stream))
                {
                    var loginString = string.Format(@"username={0}&password={1}&openid_url=&commit=Log+In", userName, password);
                    writer.Write(loginString);
                }

                request.BeginGetResponse(responseResult =>
                {
                    var response = request.EndGetResponse(responseResult);
                    var newCooks = cookies;

                    using (var stream = response.GetResponseStream())
                    using (var reader = new StreamReader(stream))
                    {
                        string line = null;
                        while (null != (line = reader.ReadLine()))
                        {
                            line = line.Trim();
                            var startIdx = line.IndexOf(SUCCESS_BEGIN);
                            if (startIdx > -1)
                            {
                                startIdx += SUCCESS_BEGIN.Length;
                                var endIdx = line.IndexOf(SUCCESS_END);
                                if (endIdx > -1)
                                {
                                    var key = line.Substring(startIdx, endIdx - startIdx);
                                    SaveSessionKey(key);
                                    onSucces();
                                    return;
                                }
                            }
                        }

                        onError();
                    }

                }, null);

            }, null);
        }

        public static void Logout()
        {
            SaveSessionKey(null);
        }

        private static void SaveSessionKey(string key)
        {
            SESSION_KEY = key;

            _storage.StoreSetting(SESSION_SETTINGS_KEY, key, () => { }, error => { });
        }

        public static bool IsLoggedIn { get { return SESSION_KEY != null; } }
        #endregion

        private static void GetEntries<T>(string url, Action<APIResponse<T>> OnResult, Action<Exception> OnError)
        {
            MakeRequest(url, OnResult, OnError);
        }

        private static void MakeRequest<T>(string url, Action<T> OnResult, Action<Exception> OnError) where T : class
        {
            try
            {
                var request = HttpWebRequest.CreateHttp(url);
                
                request.BeginGetResponse(result =>
                {
                    try
                    {
                        var req = result.AsyncState as HttpWebRequest;
                        using (var response = req.EndGetResponse(result))
                        {
                            DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(T));
                            object objResponse = jsonSerializer.ReadObject(response.GetResponseStream());
                            OnResult(objResponse as T);
                        }
                    }
                    catch (Exception inner)
                    {
                        OnError(inner);
                    }
                }, request);

            }
            catch (Exception exp) { OnError(exp); }
        }

        private static void MakeRequest<T>(string url, string data, Action<T> OnResult, Action<Exception> OnError) where T : class
        {
            try
            {
                var request = HttpWebRequest.CreateHttp(url);
                request.Method = "POST";
                request.ContentType = @"application/json; charset=utf-8";
                request.Headers[HttpRequestHeader.UserAgent] = @"iPhone Vox";


                request.BeginGetRequestStream(requestResult =>
                {
                    try
                    {
                        using (var stream = request.EndGetRequestStream(requestResult))
                        using (var writer = new StreamWriter(stream))
                        {
                            writer.Write(data);
                        }

                        request.BeginGetResponse(result =>
                        {
                            try
                            {
                                using (var response = request.EndGetResponse(result))
                                {
                                    var jsonSerializer = new DataContractJsonSerializer(typeof(T));
                                    object objResponse = jsonSerializer.ReadObject(response.GetResponseStream());
                                    OnResult(objResponse as T);
                                }
                            }
                            catch (Exception inner)
                            {
                                OnError(inner);
                            }
                        }, null);

                    }
                    catch (Exception exp)
                    {
                        OnError(exp);
                    }
                }, null);
            }
            catch (Exception exp) { OnError(exp); }
        }

        private const string API_KEY = @"rTvtu68O91ctXH9GHJVFlyxUtWjcsnuo";
        private const string BASE = @"http://www.theverge.com/api/v2/{0}?api_key={1}&iphone_client_version=2";
        private const string BASE_WITH_PAGE = BASE + @"&page={2}";
        private const string COMMENTS_ALL = @"http://www.theverge.com/api/v2/entries/{0}/comments?api_key={1}&iphone_client_version=2&offset=0";
        private const string COMMENTS_ADD = @"https://www.theverge.com/api/v2/entries/{0}/comments?api_key={1}&iphone_client_version=2";
        private const string CDN_BASE = @"http://cdn0.sbnation.com/api/v2/entries/photos?api_key={0}&iphone_client_version=2&entry_id={1}&width={2}&height={3}&format=png&compress=true";
        private const string SESSION_SETTINGS_KEY = @"SessionKey";
        
        public const string AUTH_REFERAL_URL = @"https://www.theverge.com/sessions/new?api_key=" + API_KEY;
        public const string LOGIN_SUCCESS_LAUNCH_URI = @"verge-iphone://authorize?session_key=";
        public const string LOGIN_KEY = @"session_key";

        private static string GetUrl(string type) { return string.Format(BASE, type, API_KEY); }
        private static string GetUrl(string type, int page) { return string.Format(BASE_WITH_PAGE, type, API_KEY, page); }

        
    }
}
